package cc.rengu.igas.bomp.core.service.base;

import cc.rengu.igas.bomp.common.constant.BompAppParamConstant;
import cc.rengu.igas.bomp.common.constant.BompTreeNodeConstant;
import cc.rengu.igas.bomp.common.dao.BizTmpMapper;
import cc.rengu.igas.bomp.common.dao.BompUserRoleInfoMapper;
import cc.rengu.igas.bomp.common.dao.ManagerInfoMapper;
import cc.rengu.igas.bomp.common.dao.impl.BizTmpMapperImpl;
import cc.rengu.igas.bomp.common.dao.impl.BompUserRoleInfoMapperImpl;
import cc.rengu.igas.bomp.common.dao.impl.ManagerInfoMapperImpl;
import cc.rengu.igas.bomp.common.entity.BizTmp;
import cc.rengu.igas.bomp.common.entity.ManagerInfo;
import cc.rengu.igas.bomp.common.entity.UserRoleInfo;
import cc.rengu.igas.bomp.common.enums.MchntTypeEnum;
import cc.rengu.igas.bomp.common.enums.RespCodeEnum;
import cc.rengu.igas.bomp.common.util.ChangeNextStatusUtil;
import cc.rengu.igas.bomp.core.model.UserSessionInfo;
import cc.rengu.igas.bomp.core.realize.BspsPublicService;
import cc.rengu.igas.bomp.core.realize.NormalMchntInfoService;
import cc.rengu.igas.bomp.core.realize.UserPermissionCheckService;
import cc.rengu.igas.bomp.core.realize.impl.BspsPublicServiceImpl;
import cc.rengu.igas.bomp.core.realize.impl.NormalMchntInfoServiceImpl;
import cc.rengu.igas.bomp.core.realize.impl.UserPermissionCheckServiceImpl;
import cc.rengu.igas.bomp.facade.enums.BompManageOprTypeEnum;
import cc.rengu.igas.share.core.model.MchntBindInfo;
import cc.rengu.igas.share.core.realize.impl.RiskMonitorServiceImpl;
import cc.rengu.oltp.service.base.BizBaseService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.entity.ManageTxn;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.RiskMonitorInfo;
import cc.rengu.oltp.service.realize.RiskMonitorService;
import cc.rengu.oltp.utility.annotation.BIZ_AFTER;
import cc.rengu.oltp.utility.annotation.BIZ_BEFORE;
import cc.rengu.oltp.utility.annotation.BIZ_PROCESS;
import cc.rengu.oltp.utility.util.*;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author: zhangxuran
 * @Date: 2020/4/22 10:54
 * @Description: 商户入驻，审核
 * <p>
 * 业务处理前
 * 1、检查商户状态，若商户状态
 */
public abstract class AuditService extends BizBaseService {
    @Override
    protected Object beforeBizProcess() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* Step1：业务调用前公共处理 */

        String txnNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_NUM);
        String msgSrcId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.MSG_SRC_ID);
        String traceNo = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRACE_NO);
        String mchntNo = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.MCHNT_NO);
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        String channleId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.CHANNEL_ID);
        String userId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.USER_ID);
        String userRole = null;
        ManagerInfo managerInfo;

        /* Step2：验签h5(bsps)需要验签 用户权限检查  */
        if (!BompAppParamConstant.MCMP_CHANNEL.equals(channleId)) {
            String session = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SESSION);

            /* DUBBO调用无需验签，HTTP需要进行验签 */
            String verifySignFlag = xmlTreeUtil.getXmlTreeStringValue(BompTreeNodeConstant.VERIFY_SIGN_FLAG);
            if (BompAppParamConstant.COPS_SYS_ID.equals(msgSrcId)) {
                //COPS调用,不验签
                verifySignFlag = AppParamConstant.YES;

                //需要审核
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.AUDIT_CHECKED, BompAppParamConstant.CHECKED);
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.OPR_MENU_ID, BompAppParamConstant.OPR_MENU_ID);
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.OPR_MENU_NAME, BompAppParamConstant.OPR_MENU_NAME);
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.WEB_NEXT_URL, BompAppParamConstant.WEB_NEXT_URL);
            }
            if (AppParamConstant.NO.equals(verifySignFlag)) {
                rglog.error("签名验证失败，交易码:<{}>", txnNum);
                if (StringUtil.isEmptyOrNull(session)) {
                    rglog.error("session 为null");
                    throw new BizException(RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespCode(), RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespDesc());
                }

                UserPermissionCheckService userService = new UserPermissionCheckServiceImpl();
                UserSessionInfo userSessionInfo = userService.checkUserLoginSessionStatus(instId, session, userId);
                if (null == userSessionInfo) {
                    rglog.error("根据会话标识<{}>获取会话缓存失败，转为UserSessionInfo失败。", session);
                    throw new BizException(RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespCode(), RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespDesc());
                }

                throw new BizException(RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode(), RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
            } else {
                /**
                 * 获取用户角色
                 * 1-判断用户是否为拓展经理角色
                 * 2-非拓展经理则判断角色为空继续查询是否是商户
                 * */
                ManagerInfoMapper managerInfoMapper = new ManagerInfoMapperImpl();
                if ("APP".equals(channleId)){
                    managerInfo = managerInfoMapper.selectManagerInfoById(instId,userId);
                }else {
                    managerInfo = managerInfoMapper.selectManagerInfoByOpenId(instId, userId);
                }
                if (null != managerInfo) {
                    userRole = managerInfo.getRoleType();
                }

                /*用户为商户*/
                if (StringUtil.isEmptyOrNull(userRole)) {
                    BspsPublicService bspsPublicService = new BspsPublicServiceImpl();
                    List<MchntBindInfo> mchntBindInfoList = bspsPublicService.getMchntBindInfo(instId, userId, mchntNo);
                    if (!CollectionUtils.isEmpty(mchntBindInfoList)) {
                        mchntBindInfoList = mchntBindInfoList.stream().filter(item -> MchntTypeEnum.CHAIN_MCHNT.getMchntType().equals(item.getMchntType())).collect(Collectors.toList());
                        if (!CollectionUtils.isEmpty(mchntBindInfoList)) {
                            userRole = mchntBindInfoList.get(0).getBindUserRole();
                        }
                    }
                }

                /*根据角色查询用户交易权限*/
                if (StringUtil.isEmptyOrNull(userRole)) {
                    rglog.error("用户角色查询失败", userId);
                    throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
                }

                BompUserRoleInfoMapper bompUserRoleInfoMapper = new BompUserRoleInfoMapperImpl();
                UserRoleInfo userRoleInfo = bompUserRoleInfoMapper.selectBompUserRoleInfoByRoleTypeAndTxnNum(instId, userRole, txnNum, channleId);
                if (null == userRoleInfo) {
                    rglog.info("该用户无该交易权限，userId:<{}>,mchntNo:<{}>,txnNum:<{}>", userId, mchntNo, txnNum);
                    throw new BizException(RespCodeEnum.USER_NO_AUTHORITY.getRespCode(), RespCodeEnum.USER_NO_AUTHORITY.getRespDesc());
                }
            }

            //是否需要审核 BSPS 默认设置需要审核
            xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.AUDIT_CHECKED, "1");
        }

        /* Step3：如果商户号不为空检查商户基础信息是否存在，并将商户基础信息放入XML树 */
        if (!StringUtils.isEmpty(mchntNo)) {
            NormalMchntInfoService mchntCheckService = new NormalMchntInfoServiceImpl();
            mchntCheckService.getMchntBaseInfo(instId, mchntNo);
        }

        /* 判断操作类型，若非审核，则根据oprType 校验是否存在待审核数据 */
        String oprType = xmlTreeUtil.getXmlTreeStringValue(BompTreeNodeConstant.AUDIT_OPR_TYPE);
        if (BompManageOprTypeEnum.MODIFY.getType().equals(oprType)) {
            //根据商户号查询业务临时表获取商户list数据，判断是否有处于待审核的数据，有则拒绝处理

            if (StringUtil.isEmptyOrNull(mchntNo)) {
                //商户基础信息管理 商户号在里面
                mchntNo = xmlTreeUtil.getXmlTreeStringValue("mchntBaseInfoBean/mchntNo");
            }

            BizTmpMapper bizTmpMapper = new BizTmpMapperImpl();
            List<BizTmp> bizTmpList = bizTmpMapper.selectBizTmpListByMchntNo(instId, mchntNo);
            if (!CollectionUtils.isEmpty(bizTmpList)) {
                Optional<BizTmp> matchOptional = bizTmpList.stream().filter(item -> ChangeNextStatusUtil.isAllowModifyDataByAuditAndTable(item.getAuditStatus(), item.getOprTableEname())).findFirst();
                if (matchOptional.isPresent()) {
                    rglog.error("间联商户管理失败！数据库中有数据待审核！传入instId:{},交易流水号：{}，表名:{}."
                            , instId, traceNo, matchOptional.get().getOprTableEname());
                    throw new BizException(RespCodeEnum.ILLEGAL_OPERATION.getRespCode(),
                            RespCodeEnum.ILLEGAL_OPERATION.getRespDesc());
                }
            }
        }


        /* Step2：查找BIZ_BEFORE的注解 */
        invokeBizAnnoMethod(BIZ_BEFORE.class);

        /* Step3：业务调用前客户化处理 */
        return bizBeforeCust();
    }

    @Override
    protected BizResponse callBizService(Object request) throws Exception {
        /* Step1：公共业务处理 */

        /* Step2：查找BIZ_PROCESS的注解 */
        invokeBizAnnoMethod(BIZ_PROCESS.class);

        /* Step3：业务逻辑处理 */
        return bizProcessCust(request);
    }

    @Override
    protected void afterBizProcess(BizResponse response) throws Exception {
        /* Step1：业务调用后公共处理*/

        //更新流水时，将商户号更新到Reamrk1
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 商户号 */
        String mchntNo = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.MCHNT_NO);
        /* 管理类交易 */
        ManageTxn manageTxn = new ManageTxn();
        ConvertUtil.convertOutput(manageTxn);
        manageTxn.setRemark1(mchntNo);
        ConvertUtil.convertInput(manageTxn);

        /* Step2：查找BIZ_AFTER的注解 */
        invokeBizAnnoMethod(BIZ_AFTER.class);

        /* Step3：业务调用后客户化处理 */
        bizAfterCust(response);
    }

    @Override
    protected RiskMonitorInfo getRiskMonitorInfo() {
        RiskMonitorInfo riskMonitorInfo = new RiskMonitorInfo();
        RiskMonitorService riskMonitorService = new RiskMonitorServiceImpl();
        riskMonitorInfo.setTransMonitor(riskMonitorService);
        return riskMonitorInfo;
    }

    /**
     * 业务处理前客户化处理
     *
     * @return 业务处理实体类
     */
    protected abstract Object bizBeforeCust() throws Exception;

    /**
     * 业务逻辑处理客户化处理
     *
     * @param request 业务处理前的返回实体类
     * @return 业务处理结果
     */
    protected abstract BizResponse bizProcessCust(Object request) throws Exception;

    /**
     * 业务处理后逻辑处理
     *
     * @param response 业务调用响应信息
     */
    protected abstract void bizAfterCust(Object response) throws Exception;

    protected String descriptData(String sensitiveKey, String encData) throws Exception {
        //目前只支持AES PKCS7-CBC
        String algorithmType = "AES";
        if ("AES".equals(algorithmType)) {
            String descData = AESUtil.pkcs7CbcDecryptData(Base64.decodeBase64(sensitiveKey), Base64.decodeBase64(encData));
            rglog.debug("sensitiveKey:" + sensitiveKey + ",解密字段：" + encData + ",解密值：" + descData);
            return descData;
        } else {
            byte[] bEncData = Base64.decodeBase64(encData);
            byte[] descData = new byte[bEncData.length];
            int retCode = SM4Util.sms4(bEncData, bEncData.length, ByteUtil.hexStringToByte(sensitiveKey), descData, 0);
            if (retCode == 0) {
                for (int i = 0; i < descData.length; i++) {
                    if (descData[i] == 0) {
                        return new String(descData, 0, i);
                    }
                }
                return new String(descData);
            }
        }

        return null;
    }
}
